#pragma once
#include "SFC_Inc.hpp"
#include "Setup.h"
#include "Property.h"

/** \brief Provide the basic type to support visitor.
 */

template<typename T> inline T Min(const T &a, const T &b) { return (a < b)? a : b; }
template<typename T> inline T Max(const T &a, const T &b) { return (a > b)? a : b; }

typedef unsigned int Uint32;
typedef unsigned short Uint16;
typedef unsigned char Uint8;

typedef struct
{
	Uint32 B : 8;
	Uint32 G : 8;
	Uint32 R : 8;
	Uint32 A : 8;
} Color;

typedef struct __surface_do_not_look_at_me__
{
	int w, h;
	void *data;
} *Surface;

using SFC_Bool = Property<bool>;
using SFC_Int = Property<int>;

class SFC_String : public Property<std::string>, public std::string
{
public:
	SFC_String()
	{
		this->assign("");
	}
	template<typename T>
	SFC_String(T s)
	{
		this->assign(Changed(std::string(s)));
	}
	template<typename T>
	operator T ()
	{
		return (T)*this;
	}
	template<typename T>
	SFC_String& operator=(T s)
	{
		this->assign(Changed(std::string(s)));
		return *this;
	}
};

template <typename T>
class LockingPtr {
public:
   // Constructors/destructors
   LockingPtr(volatile T& obj, std::mutex& mtx) : pObj_(const_cast<T*>(&obj)), pMtx_(&mtx) { mtx.lock(); }
   ~LockingPtr() { pMtx_->unlock(); }
   // Pointer behavior
   T& operator*() { return *pObj_; }
   T* operator->() { return pObj_; }
private:
   T* pObj_;
   std::mutex* pMtx_;
   LockingPtr(const LockingPtr&);
   LockingPtr& operator=(const LockingPtr&);
};

template<typename MemType>
class __SFC_Point
{
private:
	inline void Setk(MemType x, MemType y)
	{
		this->x = x; this->y = y;
	}
public:
	MemType x, y;
	__SFC_Point() : __SFC_Point(0, 0) {}
	__SFC_Point(MemType x, MemType y) : x(x), y(y) { }
	template<typename T>
	__SFC_Point(const T &Point) : x(Point.x), y(Point.y) { }
	__SFC_Point(const __SFC_Point<MemType>& P)
	{
		Setk(P.x, P.y);
	}
	__SFC_Point& operator=(__SFC_Point & P)
	{
		Setk(P.x, P.y);
		return *this;
	}
	__SFC_Point& operator=(__SFC_Point && P)
	{
		Setk(P.x, P.y);
		return *this;
	}
	template<typename T>
	operator T ()
	{
		return {this->x, this->y};
	}
};
using SFC_Point = __SFC_Point<int>;
using SFC_Point_X = __SFC_Point<SFC_Int>;

template<typename MemType>
class __SFC_Rect
{
private:
	inline void Setk(MemType x, MemType y, MemType w, MemType h)
	{
		this->x = x; this->y = y;
		this->w = w; this->h = h;
	}
public:
	MemType x, y, w, h;
	__SFC_Rect() : __SFC_Rect(0, 0, 0, 0) {}
	__SFC_Rect(MemType x, MemType y, MemType w, MemType h) : x(x), y(y), w(w), h(h) { }
	template<typename T>
	__SFC_Rect(const T &Rect) : x(Rect.x), y(Rect.y), w(Rect.w), h(Rect.h) { }
	__SFC_Rect(const __SFC_Rect<MemType>& R)
	{
		Setk(R.x, R.y, R.w, R.h);
	}
	__SFC_Rect& operator=(__SFC_Rect & R)
	{
		Setk(R.x, R.y, R.w, R.h);
		return *this;
	}
	__SFC_Rect& operator=(__SFC_Rect && R)
	{
		Setk(R.x, R.y, R.w, R.h);
		return *this;
	}
	__SFC_Rect operator&(const __SFC_Rect & R)
	{
		__SFC_Rect<MemType> rect;
		rect.x = Max(this->x, R.x);
		rect.y = Max(this->y, R.y);
		int x2 = Min(this->x + this->w, R.x + R.w);
		int y2 = Min(this->y + this->h, R.y + R.h);
		rect.w = x2 - x;
		rect.h = y2 - y;
		if (rect.w < (MemType)0 || rect.h < (MemType)0)
		{
			rect = {(MemType)0, (MemType)0, (MemType)0, (MemType)0};
		}
		return rect;
	}
	template<typename T>
	operator T ()
	{
		return {this->x, this->y, this->w, this->h};
	}
	template<typename Point>
	bool Inside(const Point& P)
	{
		return
		(
			(P.x >= this->x)
			&&
			(P.x <= (this->x + this->w))
		)
		&&
		(
			(P.y >= this->y)
			&&
			(P.y <= (this->y + this->h))
		);
	}
};
using SFC_Rect = __SFC_Rect<int>;
using SFC_Rect_X = __SFC_Rect<SFC_Int>;

template <typename T>
class BlockingQueue
{
private:
	BlockingQueue(const BlockingQueue &rhs);
	BlockingQueue& operator=(const BlockingQueue &rhs);
	mutable std::mutex _mutex;
	std::condition_variable _condvar;
	std::list<T> _queue;
public:
	BlockingQueue() : _mutex(), _condvar(), _queue() { }

	void Put(const T &task)
	{
		{
			std::lock_guard<std::mutex> lock(_mutex);
			_queue.push_back(task);
		}
		_condvar.notify_all();
	}

	T Get()
	{
		std::unique_lock<std::mutex> lock(_mutex);
		_condvar.wait(lock,[this] { return !_queue.empty(); });
		SFC_assert(!_queue.empty());
		T front(_queue.front());
		_queue.pop_front();
		return front;
	}

	size_t Size() const
	{
		std::lock_guard < std::mutex > lock(_mutex);
		return _queue.size();
	}
};

#include "SFC_Event.h"